/**
 * YIDATA集成平台
 *
 * http://yiidata.com
 *
 *
 */

package com.jeecg.modules.jmreport.config;

import com.alibaba.fastjson.JSONObject;
import com.google.common.io.CharStreams;
import com.google.common.io.LineProcessor;
import com.yiidata.intergration.common.utils.Constant;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * jwt工具类
 *
 * @author zhenqin
 */
@ConfigurationProperties(prefix = "jeecg.jwt")
@Component
public class JwtUtils {
    private static Logger logger = LoggerFactory.getLogger(JwtUtils.class);


    /**
     * 密文盐值，可能是公钥
     */
    private String secret;

    /**
     * 过期时间，理论上 2 小时
     */
    private long expire = 7200;

    /**
     * Header
     */
    private String header = Constant.AUTH_TOKEN;


    /**
     * 密钥二进制字节
     */
    private byte[] secretBytes;


    /**
     * 生成jwt token
     * @param userId 用户ID
     * @param userInfo 用户信息
     */
    public String generateToken(@NonNull String userId, @NonNull JSONObject userInfo) {
        // 过期时间单位是秒
        return generateToken(userId, userInfo, expire, TimeUnit.SECONDS);
    }


    /**
     * 生成jwt token
     * @param userId 用户ID
     * @param userInfo 用户信息
     * @param time 过期时间
     * @param unit 时间单位
     * @return 返回生成的 Token
     */
    public String generateToken(@NonNull String userId, @NonNull JSONObject userInfo, long time, TimeUnit unit) {
        Date nowDate = new Date();
        //过期时间
        Date expireDate = new Date(nowDate.getTime() + unit.toMillis(time));

        return Jwts.builder()
                .setHeaderParam("typ", "JWT")
                .setSubject(userInfo.toJSONString())
                .setAudience(userId)
                .setIssuedAt(nowDate)
                .setExpiration(expireDate)
                .signWith(SignatureAlgorithm.HS512, getSecretBytes())
                .compact();
    }

    public Claims getClaimByToken(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(getSecretBytes())
                    .parseClaimsJws(token)
                    .getBody();
        }catch (Exception e){
            logger.debug("validate is token error ", e);
            return null;
        }
    }

    /**
     * token是否过期
     * @return  true：过期
     */
    public boolean isTokenExpired(@NonNull Date expiration) {
        return new Date().after(expiration);
    }


    /**
     * token是否过期
     * @return  true：过期
     */
    public boolean isTokenExpired(@NonNull String token) {
        final Claims claimByToken = getClaimByToken(token);
        return new Date().after(claimByToken.getExpiration());
    }


    /**
     * 获取字节
     * @return
     */
    public byte[] getSecretBytes() {
        if(this.secretBytes == null) {
            File keyFile = new File(secret);
            if((secret.endsWith(".perm") || secret.endsWith(".key")) &&
                    keyFile.exists() &&  keyFile.isFile()) {
                try {
                    this.secretBytes = load(keyFile);
                } catch (Exception e) {
                    throw new IllegalStateException("can not read key file: " + secret, e);
                }
            } else {
                this.secretBytes = secret.getBytes(StandardCharsets.UTF_8);
            }
        }
        return secretBytes;
    }


    /**
     * 从文件加载 License
     * @param keyFile
     * @return
     * @throws IOException
     */
    public static byte[] load(File keyFile) throws IOException {
        try(InputStream in = new FileInputStream(keyFile)) {
            return load(in);
        }
    }


    /**
     * 加载 License
     * @param in
     * @return
     */
    public static byte[] load(InputStream in) throws IOException {
        return CharStreams.readLines(new BufferedReader(
                        new InputStreamReader(in, StandardCharsets.UTF_8)),
                new LineProcessor<byte[]>() {


                    private ByteArrayOutputStream buff = new ByteArrayOutputStream();

                    @Override
                    public boolean processLine(String line) throws IOException {
                        // SSL NED
                        if (line.startsWith("-----END")) {
                            return false;
                        }

                        // SSL Comment
                        if (line.startsWith("---") || line.startsWith("//") || line.startsWith("#")) {
                            return true;
                        }
                        buff.write(line.getBytes(StandardCharsets.UTF_8));
                        return true;
                    }

                    @Override
                    public byte[] getResult() {
                        return buff.toByteArray();
                    }
                });
    }


    public String getSecret() {
        return secret;
    }

    public void setSecret(String secret) {
        this.secret = secret;
    }

    public long getExpire() {
        return expire;
    }

    public void setExpire(long expire) {
        this.expire = expire;
    }

    public String getHeader() {
        return header;
    }

    public void setHeader(String header) {
        this.header = header;
    }
}
